const path = require('path')

const utils = require(process.cwd() + '/src/utils/index')
const config = require(process.cwd() + '/src/config/config')

const Tile = require('./object/tile')

const Env = require('./env')

// 地图结构
class Map {
  constructor (options = {}) {
    // 初始化
    this.initMap(options)
    /*
      设定文件 /maps/mapsSetting.json
      地图文件所在路径 /maps/
      地图文件前缀 map
    */
    // 初始化地图目录数据
    const settingPath = config.mapsPath + 'mapsSetting.json'
    // 初始化地图输出目录
    const outputPath = config.outputPath + 'maps/'
    // 目录文件结构
    this.folder = new utils.Folder(settingPath, outputPath, config.mapsPath, 'map')
    // 地图的文件,为null代表没有文件
    this.file = null
  }
  
  // 初始化预备资源字段
  static initPreparations () {
    return {
      // 环境资源
      env: [],
      // 角色资源
      roles: [],
      // 动态互动资源
      dynamic: []
    }
  }
  
  // 初始化地图数据
  initMap (options = {}) {
    let data =  {
      id: options.id ? options.id : null,
      name: options.name ? options.name : null,
      rowNum: options.row ? options.row : 0,
      colNum: options.col ? options.col : 0,
      // 预备资源列表
      preparations: Map.initPreparations(),
      // 地图图块列表
      items: [],
    }
    
    // 初始化每一个图块的数据
    const len = data.rowNum * data.colNum
    for (let i = 0; i < len; i++) {
      const rowIndex = Math.floor(i / data.colNum)
      const colIndex = i % data.colNum
      data.items.push({
        rowIndex: rowIndex,
        colIndex: colIndex,
        /*
          对象列表共有5层
          1.地表环境
          2.装饰环境1
          3.装饰环境2
          4.互动
          5.角色
          创建地图是会先放完所有的1,再放所有的2,以此类推,在layer相同的情况下,5是最上面的一层
          层并不决定覆盖次序,真正决定覆盖次序的是layer
          角色因为是动态创建,所以角色单独存放于一个角色层,不会和其他静态事物一样添加进大舞台,角色层的layer为30,layer大于30的其他事物将覆盖所有角色
        */
        objects: []
      })
    }
    
    // 地图的详细数据在mapData字段
    this.mapData = data
  }
  
  // 以指定id初始化地图数据
  async init (id = null) {
    await this.folder.init()
    if (!id) {
      return
    }
    
    // 获取地图目录文件中,该地图的节点
    const map = this.folder.getById(id)
    if (!map) {
      // 地图不存在或者文件不存在
      return Promise.reject(utils.error('MAP_NO_EXISTS', '地图不存在', id))
    }
    
    if (!map.file) {
      // 地图文件不存在,不需要进一步读取,获取id和name,保证数据完整
      this.mapData.id = id
      this.mapData.name = map.name
      return
    }
    
    // file字段不为null代表成功读取了文件
    this.file = map.file
    
    let data = await this.folder.getFileData(id)
    this.setMap(data)
    // 读取到数据后立即解压
    await this.unzip()
  }
  
  
  // 保存当前数据到地图文件
  async save () {
    // 没有有效的地图文件
    if (!this.mapData.id) {
      return Promise.reject(utils.error('MAP_UNDEFINED', '还没生成有效的地图数据'))
    }
    
    // 地图没有名称
    if (!this.mapData.name) {
      const map = this.folder.getById(this.mapData.id)
      if (!map) {
        // 地图不存在或者文件不存在
        return Promise.reject(utils.error('MAP_NO_EXISTS', '地图不存在', this.mapData.id))
      }
      this.mapData.name = map.name
    }
    console.log(2)
    // 保存前务必确保压缩
    this.zip()
    // 保存文件
    console.log(3)
    await this.folder.addFile(this.mapData.id, this.mapData)
  }
  
  async output () {
    // 没有有效的地图文件
    if (!this.mapData.id) {
      return Promise.reject(utils.error('MAP_UNDEFINED', '还没生成有效的地图数据'))
    }
    
    // 输出时不需要进行压缩
    await this.folder.output(this.mapData.id, this.mapData)
  }
  
  // 对当前地图进行重命名
  async rename (name) {
    // 没有有效的地图文件
    if (!this.mapData.id) {
      return Promise.reject(utils.error('MAP_UNDEFINED', '还没生成有效的地图数据'))
    }
    
    this.mapData.name = name
    await this.folder.rename(this.mapData.id, name)
    
    // 地图数据发生更改,保存地图数据到文件
    // 只是修改名称,没有修改资源地址,不需要zip操作
    await this.save()
  }
  
  // 删除当前数据对应的文件
  async delete () {
    await this.folder.delete(this.mapData.id)
  }
  
  // 添加一个全新的地图(不含文件)
  // 在id为parentId节点中插入名为name的新节点
  async addNewMap (parentId, name) {
    // console.log(this.folder)
    await this.folder.addChild(parentId, name)
  }
  
  // 设置地图数据
  setMap (data) {
    for (let key in data) {
      this.mapData[key] = data[key]
    }
  }
  
  // 解压数据,返回前端可以识别的资源地址
  async unzip () {
    const len = this.mapData.items.length
    for (let i = 0; i < len; i++) {
      // items里的objects对象数组中有带格式化的信息
      const objLen = this.mapData.items[i].objects.length
      for (let j = 0; j < objLen; j++) {
        // 注意地图objects列表存在空对象的可能性
        if (this.mapData.items[i].objects[j]) {
          // 串行执行,避免过多次读取数据
          await Tile.unzip(this.mapData.items[i].objects[j])
        }
      }
    }
  }
  
  // 压缩数据
  zip () {
    const len = this.mapData.items.length
    for (let i = 0; i < len; i++) {
      // items里的objects对象数组中有带格式化的信息
      const objLen = this.mapData.items[i].objects.length
      for (let j = 0; j < objLen; j++) {
        // 注意地图objects列表存在空对象的可能性
        if (this.mapData.items[i].objects[j]) {
          // 串行执行,避免过多次读取数据
          Tile.zip(this.mapData.items[i].objects[j])
        }
      }
    }
  }
  
  // 获取地图的资源库
  static async getPreparations (preparations) {
    let result = Map.initPreparations()
    
    // 获取env
    const len = preparations.env.length
    for (let i = 0; i < len; i++) {
      const name = preparations.env[i]
      let env = new Env()
      try {
        await env.init(name)
        result.env.push(env)
      }
      catch (e) {
        // 如果出现错误,说明ENV文件名不存在,可能已经改名
        console.log(e)
      }
    }
    
    // 获取角色roles
    
    // 获取互动triggers
    
    // 返回结果
    return result
  }
  
  // 获取所有有文件的地图ID列表
  async getAllMaps () {
    let maps = await this.folder.getFiles()
    maps = maps.map((map) => {
      const tail = path.extname(map)
      map = path.basename(map, tail)
      let arr = map.split('_')
      return arr[arr.length - 1]
    })
    return maps
  }
}

module.exports = Map